package edu.indiana.application;

import org.opencyc.api.CycAccess;
import org.opencyc.api.CycApiException;
import org.opencyc.api.CycConnection;
import org.opencyc.api.CycObjectFactory;
import org.opencyc.cycobject.CycConstant;
import org.opencyc.cycobject.CycFort;
import org.opencyc.cycobject.CycList;
import org.opencyc.cycobject.CycSymbol;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;


/**
 * This class talks to OpenCyc. The main attribute is CycAccess. 
 *
 */
public class CycTalk {
	CycAccess access;
    UserInterface userInterface;
    
    /*** Create the cycAccess connection to converse with cyc
     * Also copy the UI object to converse with the UI
     * @param ui
     * @throws UnknownHostException
     * @throws CycApiException
     * @throws IOException
     ***/
    
    public CycTalk(UserInterface ui){
        try {
                        this.access = new CycAccess(CycConnection.DEFAULT_HOSTNAME, CycConnection.DEFAULT_BASE_PORT);
                } catch (UnknownHostException e) {
                        e.printStackTrace();
                } catch (CycApiException e) {
                        e.printStackTrace();
                } catch (IOException e) {
                        e.printStackTrace();
                }
        this.userInterface = ui;
    }

    
	/***
	 * search for a specific constant with a given string - 
	 * if the constant is not found use the queryTerm to return possible choices the user can select
	 * @param constant
	 * @throws Exception
	 ***/
    
	public void getGenls(String constant) throws Exception {
        System.out.println("The text box entry " + constant);
        try {
			System.out.println(access.getAllGenls(access.getKnownConstantByName(constant)));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			throw new Exception(e.getMessage() + "Constant does not exist, query for related constants");
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
	
	
	/**
	 * Checks whether a constant exists or not
	 * @param constant
	 * @return boolean
	 */
	public boolean isAConstant(String constant){
        CycConstant cycConstant = null;
        try {
                cycConstant = access.getConstantByName(constant);
        } catch (UnknownHostException e) {
                e.printStackTrace();
        } catch (CycApiException e) {
                e.printStackTrace();
        } catch (IOException e) {
                e.printStackTrace();
        }
        if(cycConstant!=null) {
                return true;
        }
        return false;
	}

	
	/***
	 * search for the query term and return all possible constants
	 * @param constant
	 * @throws UnknownHostException
	 * @throws CycApiException
	 * @throws IOException
	 ***/
	public Object[] queryTerm(String constant) {
        final CycSymbol CONSTANT_COMPLETE = CycObjectFactory.makeCycSymbol("CONSTANT-COMPLETE");
        CycList constants = null;
        try {
        		CycList test = CycList.makeCycList(CONSTANT_COMPLETE, constant);
        		System.out.println(test.toString());
                constants = access.converseList(CycList.makeCycList(CONSTANT_COMPLETE, constant));
        } catch (UnknownHostException e) {
                e.printStackTrace();
        } catch (CycApiException e) {
                e.printStackTrace();
        } catch (IOException e) {
                e.printStackTrace();
        }
        return constants.toArray();
	}

	
	/***
	 * Creating a Microtheory - Takes in a input MicroTheory Name, Comment, The isaMt and the genMt
	 * Needs both isa and genMt
	 * @param mt
	 * @param comment
	 * @param isaMt
	 * @param genMt
	 * @throws UnknownHostException
	 * @throws CycApiException
	 * @throws IOException
	 */
	public void createMT(String mt, String comment, String isaMt, String genMt) throws UnknownHostException, CycApiException, IOException {
		ArrayList genlMts = new ArrayList();
		CycConstant isaMT = access.getKnownConstantByName(isaMt);
		CycConstant genMT = access.getKnownConstantByName(genMt);
		genlMts.add(genMT);
	
		access.createMicrotheory(mt, comment, isaMT, genlMts);
	}
	
	/***
	 * Creating a Microtheory system - Takes in a input MicroTheory Name, Comment, and the genMt.
	 * 	The premise here is that there is no isaMt for this, it a entirely new area we are exploring,
	 *  but it needs to have a generalization 
	 * @param mt
	 * @param comment
	 * @param genMt
	 * @throws UnknownHostException
	 * @throws CycApiException
	 * @throws IOException
	 */

	public void createMTSystem(String mt, String comment, String genMt) throws UnknownHostException, CycApiException, IOException {
		ArrayList genlMts = new ArrayList();
		CycConstant genMT = access.getKnownConstantByName(genMt);
		genlMts.add(genMT);
		access.createMicrotheorySystem(mt, comment, genlMts);
	}
	
	
	/***
	 * creating a constant - needs to specify either the isa or genls
	 * @param name
	 * @param comment
	 * @param Mt
	 * @param isa
	 * @param Genls
	 * @throws Exception
	 */
	public void createConstant(String name, String comment, String microTheory, String associatedConstant, String function) throws Exception{
        access.createNewPermanent(name);
        //access.assertComment(name, comment, microTheory);
        if(function.equalsIgnoreCase("genls"))
                access.assertGenls(name, associatedConstant);
        else if(function.equalsIgnoreCase("isA"))
                access.assertIsa(name, associatedConstant);
        else
                throw new Exception("Either Instance or Generalization is needed to create a constant");
	}

	
	/***
	 * creating a FUNCTION - This is a little complex
	 * The premise of a relationship is specified with the number of arguments 
	 * and optionally also the types of arguments it can take.
	 * This helps is constraining the functions, 
	 * for generality we can have it take an specified number of arguments and arbitrary types
	 * @param name
	 * @param nargs
	 * @param values
	 * @param params
	 * @throws UnknownHostException
	 * @throws CycApiException
	 * @throws IOException
	 */
	public void createFunction(String name, int nargs, List<String> argumentValues) {
		try {
			access.assertIsa(name,"#$Function-Denotational");
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			CycFort arity = access.getKnownConstantByName("arity");
			CycFort fname = access.getKnownConstantByName(name);
			fname.cyclify();
			CycSymbol arg = CycObjectFactory.makeCycSymbol(Integer.toString(nargs));
			CycFort mtname = access.getKnownConstantByName("UniversalVocabularyMt");
			CycList gaflist = CycList.makeCycList(arity,fname,arg); 
			System.out.println(gaflist.toString());
			access.assertGaf(gaflist,mtname);
			for(int i=0;i<argumentValues.size();i++) {
				CycFort argn = access.getKnownConstantByName("arg"+(i+1)+"Isa");
				argn.cyclify();
				CycFort argin = access.getKnownConstantByName(argumentValues.get(i));
				argin.cyclify();
				CycList gafList2 = CycList.makeCycList(argn, fname, argin);
				access.assertGaf(gafList2, mtname);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	/***
	 * creating a Predicate - This is a little complex
	 * The premise of a relationship is specified with the number of arguments 
	 * and optionally also the types of arguments it can take.
	 * This helps is constraining the functions, 
	 * for generality we can have it take an specified number of arguments and arbitrary types
	 * @param name
	 * @param nargs
	 * @param argumentValues
	 * @param params
	 * @throws UnknownHostException
	 * @throws CycApiException
	 * @throws IOException
	 */
	public void createPredicate(String name, int nargs, List<String> argumentValues){
		try {
			access.assertIsa(name,"#$Predicate");
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
		} catch (CycApiException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		try {
			CycFort arity = access.getKnownConstantByName("arity");
			CycFort fname = access.getKnownConstantByName(name);
			fname.cyclify();
			CycSymbol arg = CycObjectFactory.makeCycSymbol(Integer.toString(nargs));
			CycFort mtname = access.getKnownConstantByName("UniversalVocabularyMt");
			CycList gaflist = CycList.makeCycList(arity,fname,arg); 
			System.out.println(gaflist.toString());
			access.assertGaf(gaflist,mtname);
			for(int i=0;i<argumentValues.size();i++) {
				CycFort argn = access.getKnownConstantByName("arg"+(i+1)+"Isa");
				argn.cyclify();
				CycFort argin = access.getKnownConstantByName(argumentValues.get(i));
				argin.cyclify();
				CycList gafList2 = CycList.makeCycList(argn, fname, argin);
				access.assertGaf(gafList2, mtname);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (CycApiException e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
}